#include <stdlib.h>
#include <stdio.h>

#include "process_manager.h"

/*
 *Setup the memory for process manager to use
 */
int setup()
{
	int i = 0;
	struct process_struct *process;

	resources = (char **)malloc(128 * sizeof(char **));

	ready_size = 0;
	waiting_size = 0;
	terminated_size = 0;

	resource_size = 0;

	/*creating 128 process that will be used in the simulation */
	for (i; i < 128; i++)
	{
        process = (struct process_struct *) malloc(sizeof(struct process_struct));

        process->PID = i;
		process->process_state = Terminated;
		terminated_queue = enqueue(&terminated_queue, process, &terminated_size);

        log_queue("En", i, Terminated);
	}

    /*Create 10 Groups */
    setup_groups(10);

	return 0;
}

/*
 *Used to add a process to the Ready Queue
 */
int add_process()
{
    int i;
	if (terminated_size == 0)
	{
		 /*LOG*/
        log_error("Process can not be added, out of process space");

	} else {
		//dequeue process
		struct process_struct *dequeued = dequeue(&terminated_queue, &terminated_size);
		dequeued->process_state = Ready;

	    i = (rand() % 40);
        dequeued->run_time = i;

		ready_queue = enqueue(&ready_queue, dequeued, &ready_size);

		execute_management();

        log_queue("De", dequeued->PID, Terminated);
        log_queue("En", dequeued->PID, Ready);

	}

	return 1;
}

/*
 * Enter a specific process PID
 */
int add_process_pid(int pid)
{
	int i = 0;
	struct process_struct *process;

    //If process is not in terminated then it is being used
    process = find_node(&terminated_queue, &terminated_size, pid);

    if(NULL == process)
    {
        log_error("Process is not in Terminated Queue and can not be added by User");
        return -1;
    }

    enqueue(&ready_queue, process, &ready_size);

    return pid;
}

/*
 * Enter a specific process PID
 */
int add_process_pid_gid(int pid, int gid)
{
	int i = 0;
	struct process_struct *process;

    //If process is not in terminated then it is being used
    process = find_node(&terminated_queue, &terminated_size, pid);

    if(NULL == process)
    {
        printf("the given pid <%d> is already being used\n", pid);
        log_error("Process is not in Terminated Queue and can not be added by User");
        return -1;
    }

    add_to_group(process, &ready_size);

    return 1;
}

/*
 *Pass
 *
 *no new process is being added but the process manager still runs, moving 
 *process out of ready and onto the running for a given amount of time; checking
 *to see if resources are free, so it can move process from waiting to ready;
 *also terminating any process, etc. 
 */

/*
 *Execute Management
 * 1. Dequeue a Process from Ready 
 * 2. Execute that Process 
 * 3. Move Process off Running and into either Ready, Waiting, or Terminated 
 */
int execute_management()
{
	if (ready_size == 0)
	{
		log_error("Can not excute management when no process exist in Ready Queue");

	} else {
		// If ready queue is 0, do nothing
		struct process_struct *process;

		//dequeue
		process = dequeue(&ready_queue, &ready_size);
		process->process_state = Running;

        //run 
        log_queue("En", process->PID, Running);
		process = run_process(process);

		switch (process->process_state)
		{
		case Ready:
			//nothing to do move back onto the Ready queue
			ready_queue = enqueue(&ready_queue, process, &ready_size);

            log_queue("De", process->PID, Running);
            log_queue("En", process->PID, Ready);

			break;
		case Waiting:
			//move onto the waiting queue 
			waiting_queue = enqueue(&waiting_queue, process, &waiting_size);

            log_queue("De", process->PID, Running);
            log_queue("En", process->PID, Waiting);

			break;
		case Terminated:
			if (process->resource != NULL)
			{
				move_from_waiting_to_ready_based_on_resource(process->resource);
				remove_resource(process->resource);

				free(process->resource);
			}
			//move onto the teminated queue 
			terminated_queue = enqueue(&terminated_queue, process, &terminated_size);

            log_queue("De", process->PID, Running);
            log_queue("En", process->PID, Terminated);

			break;
		}
	}

	return 1;
}

/*
 *Run Process
 *run a process
 *currently that means subtract some time off the process's run_time and add it 
 *to CPU_time. The process may also spontaneously request a resource
 */
struct process_struct *run_process(struct process_struct *process)
{
	int i;
    char notice[46]   = "Resource <FILE?> was granted to process <___>";
    char notice_b[55] = "<___> is now waiting to get access to resource <FILE?>";

	/*Change the process status */
	process->run_time = (process->run_time - 5);
	process->CPU_time = (process->CPU_time + 5);

	/*terminate if the process has run out of time */
	if (process->run_time < 0)
	{
		process->process_state = Terminated;

		return process;
	}

	i = (rand() % 40);

	if (i > 20 && (process->resource == NULL))
	{
		process->resource = (char *)malloc(6 * sizeof(char));
		copy_string(process->resource, "FILE", 5);
		process->resource[4] = (i % 10) + 48;	//store the resource as 'FILE[0..9]', 48
                                                //is where 0 starts in ascii
		process->resource[5] = 0;	//store the resource as 'FILE[0..9]', 48


		i = resource_exists(process->resource);
		if (i == 0)
		{
			process->process_state = Ready;
			add_resource(process->resource);

            log_resource(process->resource, process->PID); 

		} else
		{
			process->process_state = Waiting;
            
            log_waiting(process->resource, process->PID);
		}

	} else
	{
		process->process_state = Ready;
	}

	return process;
}

/*
 * ---------------------------------
 *| Resource Methods                |
 *| Starts                          |
 * ---------------------------------
 */

/*
 *Resouce Exists
 *check to see if the given resource is already in the resource array
 *return 1, if the resource is already in teh array 
 *return 0, if the resource is not in the array  
 */
int resource_exists(char *resource)
{
	int i = 0;
	for (i; i < resource_size; i++)
	{
		if (is_same_string(resource, resources[i], 5))
		{
			return 1;
		}
	}

	return 0;
}

/*
 *Add Resource
 *add a resource to the resource array 
 */
int add_resource(char *resource)
{
	resources[resource_size] = resource;
	resource_size++;

	return 1;
}

/*
 *Remove Resource
 *remove a given resource from the resource array
 *also move up all the other resources on spot, so as to keep the array continuous
 */
int remove_resource(char *resource)
{
	int i = 0;

    log_notice(resource);

	for (i; i < resource_size; i++)
	{
		if (is_same_string(resource, resources[i], 5))
		{
			break;
		}
	}

	//free(resources[i]);

	for (i; i < (resource_size - 1); i++)
	{
		resources[i] = resources[i + 1];
	}

	resource_size--;

	return 1;
}

/*
 *Move from Waiting to Ready based on Resource
 *moves one process out of the waiting queue and into ready queue if the 
 *process has a resource that matches the one given
 */
int move_from_waiting_to_ready_based_on_resource(char *resource)
{
	int pid;
	struct process_struct *process;

	/*search waiting queue */
	pid = search_queue_for_resource(waiting_queue, waiting_size, resource);

	/*deque from waiting, if found */
	if (pid == -1)
	{
		return 1;
	} else
	{
		//dequeue this pid
        process = remove_node(&waiting_queue, &waiting_size, pid);     

	    /*enqueue onto ready */
        process->process_state = Ready;
        enqueue(&ready_queue, process, &ready_size); 


        log_queue("De", process->PID, Waiting);
        log_queue("En", process->PID, Ready);
	}


	return 1;
}

/*
 *Search Queue for Resource 
 *search a given queue for a resource
 *return PID, if a process is found that has the same resource as was given
 *return -1, if no process was found that has the resources given
 */
int search_queue_for_resource(struct process_struct *queue, int size, char *resource)
{
	int found = 0;
	int i = 0;

	struct process_struct *process = queue;

	for (i; i < size; i++)
	{
		if (process->resource != NULL)	//does the process even have a string?
		{
			if (is_same_string(resource, process->resource, 5))	//does it match the one I want?
			{
				return process->PID;

			}
		}

		process = process->next_process;

	}

	return -1;
}

/*
 * ---------------------------------
 *| Resource Methods                |
 *| End                             |
 * ---------------------------------
 */

/*
 *------------------------------------------
 *Some simple utility functions for the menu
 *------------------------------------------
 */

/*
 *Print Stats 
 *used to show the user how where items are from the CL
 */
void print_stats()
{
	printf("The number of Ready Process is: %d\n", ready_size);
	printf("The number of Waiting Process is: %d\n", waiting_size);
	printf("The number of Terminated Process is: %d\n", terminated_size);
	printf("The number of Resources being used is: %d\n", resource_size);
}

/*
 *Print PIDS
 *loop over the queues that have alive process and show thier PIDs
 */
void print_PIDs()
{
	int i = 0;

	for (i; i < ready_size; i++)
	{
		//move across the queue and print the PIDS
	}

	i = 0;
	for (i; i < waiting_size; i++)
	{
		//move across the queue and print the PIDS
	}
}

/*
 *Print Resources
 *print all the resources being used by active process
 */
void print_resources()
{
	int i = 0;

	for (i; i < resource_size; i++)
	{
		printf("%d. %s", i, resources[i]);
	}

}

/*
 * List All 
 *
 * show all the pids being used
 */
void list_all()
{
    group_stats();

    return;
}

/*
 * List Sched 
 *
 * print the information about the next process 
 */
void list_sched()
{
   //show process and group  
   
   return;
}

/*
 * List Queue
 *
 * Print information about a given queue
 */
void list_queue(int queue)
{

}
